#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "tlpi.h"
#include <string.h>
#include <errno.h>

#define MAX_CH 100

void 
displayHelpInfo(void)
{
	printf("Format:cmd\tlock\tstart\tlen\t[whence]\n");
	printf("\tcmd: g[GETLK] | s[SETLK] | w[SETLKW]\n");
	printf("\tlock: r[READ] | w[WRITE] | u[UNLOCK]\n");
	printf("\tstart: num\n");
	printf("\tlen: num\n");
	printf("\twhence: s[SEEK_SET, default] | c[SEEK_CUR] | e[SEEK_END]\n");
}


int
main(int argc, char *argv[])
{
	int fd, numRead, cmd, status;
	char lock, cmdCh, whence, line[MAX_CH];
	struct flock fl;
	long long len, st;

	if(argc != 2)
		fatal("usage error\n");

	if((fd = open(argv[1], O_RDWR)) == -1)
		errExit("open() failed");

	printf("Enter ? for help info\n");

	for(;;)
	{
		printf("PID=%ld> ", (long)getpid());
		fflush(stdout);

		if(fgets(line, MAX_CH, stdin) == NULL)
		{
			putchar('\n');
			exit(EXIT_SUCCESS);
		}

		line[strlen(line) - 1] = '\0';

		if(*line == '\0')
			continue;

		if(line[0] == '?')
		{
			displayHelpInfo();
			continue;
		}

		whence = 's';
		numRead = sscanf(line, "%c %c %lld %lld %c", &cmdCh, &lock,
							&st, &len, &whence);
		if(numRead < 4 || strchr("gsw", cmdCh) == NULL || strchr("rwu", lock) == NULL
				|| strchr("sce", whence) == NULL)
		{
			printf("Invalid command!\n");
			displayHelpInfo();
			continue;
		}

		fl.l_start = st;
		fl.l_len = len;
		fl.l_type = (lock == 'r')? F_RDLCK : (lock == 'w')? F_WRLCK : F_UNLCK;
		fl.l_whence = (whence == 'c')? SEEK_CUR : 
						(whence == 'e')? SEEK_END : SEEK_SET;

		cmd = (cmdCh == 'g')? F_GETLK : (cmdCh == 's')? F_SETLK : F_SETLKW;
		
		status = fcntl(fd, cmd, &fl);

		if(cmd == F_GETLK)
		{
			if(status == -1)
				errExit("fcntl() F_GETLK failed");
			else
			{
				if(fl.l_type == F_UNLCK)
					printf("[PID=%ld] lock can be placed\n", (long)getpid());
				else
					printf("[PID=%ld] Denied by %s on %lld:%lld"
							"(held by PID %ld\n)", (long)getpid(),
							(fl.l_type == F_RDLCK)? "read" : "write",
							(long long)fl.l_start,
							(long long)fl.l_len, (long)fl.l_pid);
			}
		}
		else
		{
			if(status == 0)
				printf("[PID=%ld] %s\n", (long)getpid(), 
						(lock == 'u')? "unlocked" : "get lock");
			else if(errno == EAGAIN || errno == EACCES)
				printf("[PID=%ld] failed(incompatible lock)\n", 
						(long)getpid());
			else if(errno == EDEADLK)
				printf("[PID=%ld] faile(dead lock)\n", (long)getpid());
			else
				errExit("fcntl() F_SETLK(W) failed");
		}	
	}
}
